/*
	ligregni@freebsd.org
	code from BSD's fdisk used
*/

#include <sys/cdefs.h>
//__FBSDID("$FreeBSD: src/sbin/fdisk/fdisk.c,v 1.100 2011/01/25 04:35:07 sobomax Exp $");

#include <sys/disk.h>
#include <sys/disklabel.h>
#include <sys/diskmbr.h>
#include <sys/endian.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/mount.h>
#include <ctype.h>
#include <fcntl.h>
#include <err.h>
#include <errno.h>
#include <libgeom.h>
#include <paths.h>
#include <regex.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "util_fdisk.h"

int iotest;

#define NO_DISK_SECTORS ((u_int32_t)-1)
#define NO_TRACK_CYLINDERS 1023
#define NO_TRACK_HEADS 255
#define NO_TRACK_SECTORS 63
#define LBUF 100
static char lbuf[LBUF];

/*
 *
 * Ported to 386bsd by Julian Elischer  Thu Oct 15 20:26:46 PDT 1992
 *
 * 14-Dec-89  Robert Baron (rvb) at Carnegie-Mellon University
 *	Copyright (c) 1989	Robert. V. Baron
 *	Created.
 */

#define Decimal(str, ans, tmp, maxval) if (decimal(str, &tmp, ans, maxval)) ans = tmp

#define RoundCyl(x) ((((x) + cylsecs - 1) / cylsecs) * cylsecs)

#define MAX_SEC_SIZE 2048	/* maximum section size that is supported */
#define MIN_SEC_SIZE 512	/* the sector size to start sensing at */
static int secsize = 0;		/* the sensed sector size */

static char *disk;

static int cyls, sectors, heads, cylsecs, disksecs;

static int fd;

#define ACTIVE 0x80

static uint dos_cyls;
static uint dos_heads;
static uint dos_sectors;
static uint dos_cylsecs;

#define DOSSECT(s,c) ((s & 0x3f) | ((c >> 2) & 0xc0))
#define DOSCYL(c)	(c & 0xff)

#define MAX_ARGS	10

static int	current_line_number;

static int	geom_processed = 0;
static int	part_processed = 0;
static int	active_processed = 0;

typedef struct cmd {
    char		cmd;
    int			n_args;
    struct arg {
	char	argtype;
	int		arg_val;
	char	*arg_str;
    }			args[MAX_ARGS];
} CMD;

//static int B_flag  = 0;		/* replace boot code */
//static int I_flag  = 0;		/* use entire disk for FreeBSD */
//static int a_flag  = 0;		/* set active partition */
//static char *b_flag = NULL;	/* path to boot code */
//static int i_flag  = 0;		/* replace partition data */
//static int q_flag  = 0;		/* Be quiet */
//static int u_flag  = 0;		/* update partition data */
//static int s_flag  = 0;		/* Print a summary and exit */
//static int t_flag  = 0;		/* test only */
//static char *f_flag = NULL;	/* Read config info from file */
//static int v_flag  = 0;		/* Be verbose */
//static int print_config_flag = 0;

/*
 * A list of partition types, probably outdated.
 */
static const char *const part_types[256] = {
	[0x00] = "unused",
	[0x01] = "Primary DOS with 12 bit FAT",
	[0x02] = "XENIX / file system",
	[0x03] = "XENIX /usr file system",
	[0x04] = "Primary DOS with 16 bit FAT (< 32MB)",
	[0x05] = "Extended DOS",
	[0x06] = "Primary DOS, 16 bit FAT (>= 32MB)",
	[0x07] = "NTFS, OS/2 HPFS, QNX-2 (16 bit) or Advanced UNIX",
	[0x08] = "AIX file system or SplitDrive",
	[0x09] = "AIX boot partition or Coherent",
	[0x0A] = "OS/2 Boot Manager, OPUS or Coherent swap",
	[0x0B] = "DOS or Windows 95 with 32 bit FAT",
	[0x0C] = "DOS or Windows 95 with 32 bit FAT (LBA)",
	[0x0E] = "Primary 'big' DOS (>= 32MB, LBA)",
	[0x0F] = "Extended DOS (LBA)",
	[0x10] = "OPUS",
	[0x11] = "OS/2 BM: hidden DOS with 12-bit FAT",
	[0x12] = "Compaq diagnostics",
	[0x14] = "OS/2 BM: hidden DOS with 16-bit FAT (< 32MB)",
	[0x16] = "OS/2 BM: hidden DOS with 16-bit FAT (>= 32MB)",
	[0x17] = "OS/2 BM: hidden IFS (e.g. HPFS)",
	[0x18] = "AST Windows swapfile",
	[0x1b] = "ASUS Recovery partition (NTFS)",
	[0x24] = "NEC DOS",
	[0x3C] = "PartitionMagic recovery",
	[0x39] = "plan9",
	[0x40] = "VENIX 286",
	[0x41] = "Linux/MINIX (sharing disk with DRDOS)",
	[0x42] = "SFS or Linux swap (sharing disk with DRDOS)",
	[0x43] = "Linux native (sharing disk with DRDOS)",
	[0x4D] = "QNX 4.2 Primary",
	[0x4E] = "QNX 4.2 Secondary",
	[0x4F] = "QNX 4.2 Tertiary",
	[0x50] = "DM (disk manager)",
	[0x51] = "DM6 Aux1 (or Novell)",
	[0x52] = "CP/M or Microport SysV/AT",
	[0x53] = "DM6 Aux3",
	[0x54] = "DM6",
	[0x55] = "EZ-Drive (disk manager)",
	[0x56] = "Golden Bow (disk manager)",
	[0x5c] = "Priam Edisk (disk manager)", /* according to S. Widlake */
	[0x61] = "SpeedStor",
	[0x63] = "System V/386 (such as ISC UNIX), GNU HURD or Mach",
	[0x64] = "Novell Netware/286 2.xx",
	[0x65] = "Novell Netware/386 3.xx",
	[0x70] = "DiskSecure Multi-Boot",
	[0x75] = "PCIX",
	[0x77] = "QNX4.x",
	[0x78] = "QNX4.x 2nd part",
	[0x79] = "QNX4.x 3rd part",
	[0x80] = "Minix until 1.4a",
	[0x81] = "Minix since 1.4b, early Linux partition or Mitac disk manager",
	[0x82] = "Linux swap or Solaris x86",
	[0x83] = "Linux native",
	[0x84] = "OS/2 hidden C: drive",
	[0x85] = "Linux extended",
	[0x86] = "NTFS volume set??",
	[0x87] = "NTFS volume set??",
	[0x93] = "Amoeba file system",
	[0x94] = "Amoeba bad block table",
	[0x9F] = "BSD/OS",
	[0xA0] = "Suspend to Disk",
	[0xA5] = "FreeBSD/NetBSD/386BSD",
	[0xA6] = "OpenBSD",
	[0xA7] = "NeXTSTEP",
	[0xA9] = "NetBSD",
	[0xAC] = "IBM JFS",
	[0xAF] = "HFS+",
	[0xB7] = "BSDI BSD/386 file system",
	[0xB8] = "BSDI BSD/386 swap",
	[0xBE] = "Solaris x86 boot",
	[0xBF] = "Solaris x86 (new)",
	[0xC1] = "DRDOS/sec with 12-bit FAT",
	[0xC4] = "DRDOS/sec with 16-bit FAT (< 32MB)",
	[0xC6] = "DRDOS/sec with 16-bit FAT (>= 32MB)",
	[0xC7] = "Syrinx",
	[0xDB] = "CP/M, Concurrent CP/M, Concurrent DOS or CTOS",
	[0xDE] = "DELL Utilities - FAT filesystem",
	[0xE1] = "DOS access or SpeedStor with 12-bit FAT extended partition",
	[0xE3] = "DOS R/O or SpeedStor",
	[0xE4] = "SpeedStor with 16-bit FAT extended partition < 1024 cyl.",
	[0xEB] = "BeOS file system",
	[0xEE] = "EFI GPT",
	[0xEF] = "EFI System Partition",
	[0xF1] = "SpeedStor",
	[0xF2] = "DOS 3.3+ Secondary",
	[0xF4] = "SpeedStor large partition",
	[0xFE] = "SpeedStor >1024 cyl. or LANstep",
	[0xFF] = "Xenix bad blocks table",
};

static int open_disk(int);
static int get_params();
static int read_s0();
static ssize_t read_disk(off_t sector, void *buf);

int
init_disc(char *name)
{
	disk = name;
	fd = open_disk(0);

	/* (abu)use mboot.bootinst to probe for the sector size */
	if ((mboot.bootinst = malloc(MAX_SEC_SIZE)) == NULL)
		err(1, "cannot allocate buffer to determine disk sector size");
	if (read_disk(0, mboot.bootinst) == -1)
		errx(1, "could not detect sector size");
	free(mboot.bootinst);
	mboot.bootinst = NULL;

	read_s0();

	g_close(fd);

	return 0;
}

void
log_error(char *message)
{
	printf("%s\n", message);
}

static int
open_disk(int flag)
{
	int rwmode;

	/* Write mode if one of these flags are set. */
	//rwmode = (a_flag || I_flag || B_flag || flag);
	rwmode = 0;
	fd = g_open(disk, rwmode);
	/* If the mode fails, try read-only if we didn't. */
	if (fd == -1 && errno == EPERM && rwmode)
		fd = g_open(disk, 0);
	if (fd == -1 && errno == ENXIO)
		return -2;
	if (fd == -1) {
		warnx("can't open device %s", disk);
		return -1;
	}
	if (get_params() == -1) {
		warnx("can't get disk parameters on %s", disk);
		return -1;
	}
	return fd;
}

static int
read_s0()
{
	int i;

	mboot.bootinst_size = secsize;
	if (mboot.bootinst != NULL)
		free(mboot.bootinst);
	if ((mboot.bootinst = malloc(mboot.bootinst_size)) == NULL) {
		warnx("unable to allocate buffer to read fdisk "
		      "partition table");
		return -1;
	}
	if (read_disk(0, mboot.bootinst) == -1) {
		warnx("can't read fdisk partition table");
		return -1;
	}
	if (le16dec(&mboot.bootinst[DOSMAGICOFFSET]) != DOSMAGIC) {
		warnx("invalid fdisk partition table found");
		/* So should we initialize things */
		return -1;
	}
	for (i = 0; i < NDOSPART; i++)
	{
		dos_partition_dec(
		    &mboot.bootinst[DOSPARTOFF + i * DOSPARTSIZE],
		    &mboot.parts[i]);
	}
	return 0;
}

static int
get_params()
{
	int error;
	u_int u;
	off_t o;

	error = ioctl(fd, DIOCGFWSECTORS, &u);
	if (error == 0)
		sectors = dos_sectors = u;
	else
		sectors = dos_sectors = 63;

	error = ioctl(fd, DIOCGFWHEADS, &u);
	if (error == 0)
		heads = dos_heads = u;
	else
		heads = dos_heads = 255;

	dos_cylsecs = cylsecs = heads * sectors;
	disksecs = cyls * heads * sectors;

	u = g_sectorsize(fd);
	if (u <= 0)
		return (-1);

	o = g_mediasize(fd);
	if (o < 0)
		return (-1);
	disksecs = o / u;
	cyls = dos_cyls = o / (u * dos_heads * dos_sectors);

	return (disksecs);
}

static ssize_t
read_disk(off_t sector, void *buf)
{

	lseek(fd, (sector * 512), 0);
	if (secsize == 0)
		for (secsize = MIN_SEC_SIZE; secsize <= MAX_SEC_SIZE;
		     secsize *= 2) {
			/* try the read */
			int size = read(fd, buf, secsize);
			if (size == secsize)
				/* it worked so return */
				return secsize;
		}
	else
		return read(fd, buf, secsize);

	/* we failed to read at any of the sizes */
	return -1;
}

const char *
get_label_name(int index)
{
	if ( part_types[index] == NULL )
		return "unknown";
	return part_types[index];
}
